package com.jboltai.event.chain;


import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import com.jboltai.capability.message.AIIntention;
import com.jboltai.event.EventErrorType;
import com.jboltai.event.EventState;
import com.jboltai.util.share.AIEventThreadShareData;

import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 事件链节点
 */
public abstract class ChainNode<T> implements Runnable{

    /**
     * 节点执行成功之后的分支
     */
    public static final String AFTER_KEY_SUCCESS = "success";
    /**
     * 节点执行失败之后的分支
     */
    public static final String AFTER_KEY_FAIL = "fail";





    /**
     * 前置节点
     */
    protected ChainNode[] prevs;

    /**
     * 当前节点的后续关联节点
     */
    protected HashMap<String, ChainNode[]> afters;

    protected TriggerType triggerType = TriggerType.ALL_OVER;

    protected EventState state = EventState.WAITING;

    protected String id = IdUtil.fastSimpleUUID();

    protected Chain chain;

    /**
     * 是否忽略异常，即使异常了，也继续向后执行
     */
    protected boolean ignoreFail;;

    protected ReentrantLock lock = new ReentrantLock();


    /**
     * 运行
     */
    public void run() {
        //判断是否可以执行，没有前置或者 所有前置都success，就可以执行
        try {
            lock.lock();
            //链的状态不对，直接返回
            if (chain.getState() != EventState.PROCESSING) {
                return;
            }
            boolean trigger = false;

            if (this.triggerType == TriggerType.ANY) {
                trigger = prevs == null
                        || prevs.length == 0
                        || Arrays.stream(prevs).anyMatch(
                                item -> item.state == EventState.SUCCESS
                                || (item.state == EventState.FAIL && item.ignoreFail )
                                || (item.state == EventState.FAIL && ArrayUtil.contains(item.getAfterNodes(AFTER_KEY_FAIL), this))
                );

            } else {
                trigger =
                        prevs == null
                                || prevs.length == 0
                                || Arrays.stream(prevs).allMatch(item -> item.state == EventState.SUCCESS
                                || (item.state == EventState.FAIL && item.ignoreFail )
                                || (item.state == EventState.FAIL && ArrayUtil.contains(item.getAfterNodes(AFTER_KEY_FAIL), this)));

            }

            if (trigger) {
                this.setState(EventState.PROCESSING);
                AIEventThreadShareData.setShareData(chain.getThreadShareData());
                execute();
                return;
            }


        } finally {
            lock.unlock();
        }
    }


    /**
     * 执行节点的特有逻辑
     */
    protected abstract void execute();


    /**
     * 添加前置条件节点，并且仅当前置节点返回true时触发
     * @param node
     * @return
     */
    public T addPrevWhenTrue(BooleanChainNode node) {
        if (node == null ) {
            throw new IllegalArgumentException("前置节点不可为空");
        }
        //根据前置节点直接设置链
        this.setChain(node.chain);
        node.addAfterNode(this, BooleanChainNode.AFTER_KEY_TRUE);
        if (prevs == null) {
            prevs = new ChainNode[]{node};
        } else {
            prevs = ArrayUtil.append(prevs, node);
        }
        return (T) this;
    }
    /**
     * 添加前置条件节点，并且仅当前置节点返回false时触发
     * @param node
     * @return
     */
    public T addPrevWhenFalse(BooleanChainNode node) {
        if (node == null ) {
            throw new IllegalArgumentException("前置节点不可为空");
        }
        //根据前置节点直接设置链
        this.setChain(node.chain);
        node.addAfterNode(this, BooleanChainNode.AFTER_KEY_FALSE);
        if (prevs == null) {
            prevs = new ChainNode[]{node};
        } else {
            prevs = ArrayUtil.append(prevs, node);
        }
        return (T) this;
    }


    /**
     * 添加前置分支节点
     * @param node
     * @param caseName 分支名称
     */
    public T addPrevWhenCase(CaseChainNode node, String caseName) {
        if (node == null ) {
            throw new IllegalArgumentException("前置节点不可为空");
        }
        //根据前置节点直接设置链
        this.setChain(node.chain);
        node.addAfterNode(this, caseName);
        if (prevs == null) {
            prevs = new ChainNode[]{node};
        } else {
            prevs = ArrayUtil.append(prevs, node);
        }
        return (T) this;
    }
    /**
     * 当意图匹配时触发
     * @param intentionKey 匹配到的意图key
     * @param node
     * @return
     */
    public T addPrevWhenIntentionMatch(String intentionKey, IntentionChainNode node) {
        if (node == null ) {
            throw new IllegalArgumentException("前置节点不可为空");
        }
        //根据前置节点直接设置链
        this.setChain(node.chain);
        node.addAfterNode(this,intentionKey);
        if (prevs == null) {
            prevs = new ChainNode[]{node};
        } else {
            prevs = ArrayUtil.append(prevs, node);
        }
        return (T) this;
    }

    /**
     * 当意图不匹配时触发
     * @param node
     * @return
     */
    public T addPrevWhenIntentionNotMatch(IntentionChainNode node) {
        return addPrevWhenIntentionMatch(AIIntention.NO_MATCH_KEY, node);
    }


    /**
     * 添加前置节点
     * @param nodes
     */
    public T addPrevNode(ChainNode... nodes) {
        if (nodes == null && nodes.length == 0) {
            throw new IllegalArgumentException("前置节点不可为空");
        }
        for (ChainNode node : nodes) {
            //根据前置节点直接设置链
            this.setChain(node.chain);
            node.addAfterNode(this);
        }
        if (prevs == null) {
            prevs = nodes;
        } else {
            prevs = ArrayUtil.append(prevs, nodes);
        }
        return (T) this;

    }
    /**
     * 添加前置节点
     * @param nodes
     */
    public T addPrevNodeWhenFail(ChainNode... nodes) {
        if (nodes == null && nodes.length == 0) {
            throw new IllegalArgumentException("前置节点不可为空");
        }
        for (ChainNode node : nodes) {
            //根据前置节点直接设置链
            this.setChain(node.chain);
            node.addAfterNodeWhenFail(this);
        }
        if (prevs == null) {
            prevs = nodes;
        } else {
            prevs = ArrayUtil.append(prevs, nodes);
        }
        return (T) this;

    }

    /**
     * 记录success的后续节点
     * @param node
     */
    protected void addAfterNode(ChainNode node) {
        addAfterNode(node, AFTER_KEY_SUCCESS);
    }

    /**
     * 记录fail的后续节点
     * @param node
     */
    protected void addAfterNodeWhenFail(ChainNode node) {
        addAfterNode(node, AFTER_KEY_FAIL);
    }
    /**
     * 记录case分值的后续节点
     * @param node
     * @param caseName
     */
    protected void addAfterNode(ChainNode node, String caseName) {
        if (afters == null) {
            afters = new HashMap<>();
        }
        ChainNode[] nodes = afters.get(caseName);
        if (nodes == null) {
            nodes = new ChainNode[]{node};
        } else {
            nodes = ArrayUtil.append(nodes, node);
        }
        afters.put(caseName, nodes);

    }



    public ChainNode[] getPrevs() {
        return prevs;
    }

    public EventState getState() {
        return state;
    }

    public String getId() {
        return id;
    }

    public void fail(String errorMsg, Throwable e) {
        chain.removeFromRunning(this.id);
        setState(EventState.FAIL);
        ChainNode[] failAfterNodes = getAfterNodes(AFTER_KEY_FAIL);
        if (ignoreFail) {
            //忽略异常，继续按success分支走
            next();
        } else if (failAfterNodes != null && failAfterNodes.length > 0) {
            //继续按fail分支走
            for (ChainNode after : failAfterNodes) {
                this.chain.startNode(after);
            }
        }else {
            //结束整个链条
            chain.fail(EventErrorType.CHAIN_NODE_FAIL,"节点["+this.getId()+"]执行过程中发生异常" + (errorMsg == null ? "":(":"+errorMsg)), e);
            tryComplete();
        }



    }

    /**
     * 取消时干什么
     */
    protected void cancel() {

    }


    public void success() {
        chain.removeFromRunning(this.id);
        setState(EventState.SUCCESS);

        next();
    }

    protected ChainNode[] getAfterNodes(String caseName) {
        if (afters == null) {
            return null;
        }
        return afters.get(caseName);
    }

    /**
     * 执行下一个节点
     */
    protected void next() {
        ChainNode[] afterNodes = getAfterNodes(AFTER_KEY_SUCCESS);
        if (afterNodes != null && afterNodes.length > 0) {
            for (ChainNode after : afterNodes) {
                this.chain.startNode(after);
            }
        } else {
            //自己就是最后的节点了，通知父节点完事了
            tryComplete();
        }
    }



    /**
     * 尝试完结
     */
    protected void tryComplete() {

        if (chain.getState() != EventState.PROCESSING) {
            //chain 已经被改变了，不需要再通知了
            return;
        }

        boolean l = lock.tryLock();
        //锁都获取不到，肯定当前在执行中，那就不可能完结
        if (!l) return;

        try {

            boolean allAfterDone = false;

            ChainNode[] afters = null;

            if (this.state == EventState.SUCCESS) {
                afters = getAfterNodes(AFTER_KEY_SUCCESS);
            } else if (this.state == EventState.FAIL) {
                afters = getAfterNodes(AFTER_KEY_FAIL);
            }


            if (afters == null || afters.length == 0) {
                allAfterDone = true;
            } else {
                allAfterDone = Arrays.stream(afters).allMatch(item -> (
                        item.state == EventState.SUCCESS ||
                        item.state == EventState.FAIL ||
                        item.state == EventState.CANCEL
                ));
            }

            if (allAfterDone) {
                // 所有后续节点都执行完毕，有父级节点就通知父级节点检查是否可以完结
                if (this.prevs != null && this.prevs.length > 0) {
                    for (ChainNode prev : this.prevs) {
                        prev.tryComplete();
                    }
                } else {
                    chain.tryComplete();
                }
            }
        } finally {
            lock.unlock();
        }



    }

    protected void setState(EventState state) {
        this.state = state;
    }


    public T setId(String id) {
        this.id = id;
        return (T) this;
    }

    public TriggerType getTriggerType() {
        return triggerType;
    }

    public T setTriggerType(TriggerType triggerType) {
        this.triggerType = triggerType;
        return (T) this;
    }


    /**
     * 获取节点所在的链
     * @return
     */
    public Chain getChain() {
        return chain;
    }

    public boolean getIgnoreFail() {
        return ignoreFail;
    }

    public T setIgnoreFail(boolean ignoreFail) {
        this.ignoreFail = ignoreFail;
        return (T) this;
    }

    /**
     * 设置节点所在的链
     * @param newChain
     */
    protected void setChain(Chain newChain) {
        if (newChain != null && this.chain == null) {
            this.chain = newChain;
            if (this.afters != null) {
                this.afters.values().stream().forEach(nodes -> {
                    for (ChainNode chainNode : nodes) {
                        chainNode.setChain(newChain);
                    }
                });
            }

        } else if (newChain != null && newChain != this.chain) {
            throw new IllegalArgumentException("前置节点所在的链与当前节点不一致，无法添加");
        }

    }

}
